package com.simpleframe.persistence.hibernate;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.hibernate.*;
import org.hibernate.criterion.*;
import org.hibernate.transform.Transformers;
import org.hibernate.type.NullableType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.simpleframe.exception.SimpleframeException;

/**
 * 泛型Hibernate DAO类
 * 
 * @author DigitalSonic
 */
@SuppressWarnings("unchecked")
public class HibernateGenericDao<T, ID extends Serializable> extends HibernateDaoSupport {
    private Class<T> pojoClass;
    
    @Autowired
	public void setFactory(SessionFactory sessionFactory){
		super.setSessionFactory(sessionFactory);
	}
    
    /**
     * 初始化DAO，获取POJO类型
     */
    public HibernateGenericDao() {
//        this.pojoClass = (Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        this.pojoClass = GenericsUtils.getSuperClassGenricType(getClass());
    }
    
    /**
     * 获得该DAO对应的POJO类型
     */
    public Class<T> getPojoClass() {
        return this.pojoClass;
    }
    
    /**
     * 获得该DAO对应的POJO类型名
     */
    public String getPojoClassName() {
        return getPojoClass().getName();
    }

    //加载对象
    
    /**
     * 加载所有的对象
     */
    public List<T> loadAll() {
        return (List<T>)getHibernateTemplate().loadAll(getPojoClass());
    }
    
    /**
     * 根据hql查询
     *
     * @param values 可变参数
     */
    public List find(String hql, Object... values) {
        return getHibernateTemplate().find(hql, values);
    }
    
    /**
     * 根据条件加载对象
     * 
     * @param criteria Criteria实例
     */
    public List<T> findByCriteria(final Criteria criteria) {
        List list = criteria.list(); 
        return transformResults(list);
    }
    
    /**
     * 根据条件加载对象
     * @param detachedCriteria DetachedCriteria实例
     */
    public List<T> findByCriteria(final DetachedCriteria detachedCriteria) {
        return (List<T>) getHibernateTemplate().execute(new HibernateCallback() {
               public Object doInHibernate(Session session) throws HibernateException {
                   Criteria criteria = detachedCriteria.getExecutableCriteria(session);
                   List list = criteria.list(); 
                   return transformResults(list); 
               }
           });
    }
    
    /**
     * 根据给定的实例查找对象
     */
    public List<T> findByExample(T instance) {
        List<T> results = (List<T>)getHibernateTemplate().findByExample(instance);
        return results;
    }    
    
    /**
     * 根据ID查找对象
     */
    public T findById(ID id) {
        return (T) getHibernateTemplate().get(getPojoClassName(), id);
    }
    
    /**
     * 根据某个具体属性进行查找
     */
    public List<T> findByProperty(String propertyName, Object value) {
       String queryString = "from " + getPojoClassName() + " as model where model." 
                               + propertyName + "= ?";
       return (List<T>)getHibernateTemplate().find(queryString, value);
    }
    
    //新建、修改、删除
    
    /**
     * 新建对象实例化
     */
    public ID save(T transientInstance) {
        return (ID)getHibernateTemplate().save(transientInstance);
    }
    
    /**
     * 新建对象或修改对象
     */
    public void saevOrUpdate(T transientInstance) {
    	getHibernateTemplate().saveOrUpdate(transientInstance);
    }
    
    /**
     * 更新已存在的对象
     */
    public void update(T transientInstance) {
        getHibernateTemplate().update(transientInstance);
    }
    
    /**
     * 删除指定ID的对象
     */
    public void delete(ID id) {
           T instance = findById(id);
           if (instance != null)
               getHibernateTemplate().delete(instance);
    }
    
    /**
     * 删除指定IDs的对象
     */
    public void deletes(ID[] ids){
    	if(ids==null){
    		throw new SimpleframeException("删除对象出错，未选择任何对象ID。");
    	}
    	for (ID id : ids) {
			delete(id);
		}
    }
    
    /**
     * 删除指定对象
     */
    public void delete(T persistentInstance) {
        getHibernateTemplate().delete(persistentInstance);
    }    
    
    //分页
    /**
     * 根据Criteria加载分页，指定页大小和起始位置
     */
//    public Page findPageByCriteria(final Criteria criteria, final int pageSize, final int startIndex) {   
//        int totalCount = getCountByCriteria(criteria);   
//        criteria.setProjection(null);
//        List items = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
//        items = transformResults(items);
//        Page ps = new Page(items, totalCount, pageSize, startIndex);
//        return ps;
//    }
    
    /**
     * 根据Criteria加载分页，默认页大小，从第0条开始
     */
//    public Page findPageByCriteria(final Criteria criteria) {   
//        return findPageByCriteria(criteria, Page.PAGESIZE, 0);   
//    }   
 
    /**
     * 根据Criteria加载分页，默认页大小，从第startIndex条开始
     */
//    public Page findPageByCriteria(final Criteria criteria, final int startIndex) {   
//        return findPageByCriteria(criteria, Page.PAGESIZE, startIndex);   
//    }
    
    /**
     * 根据Criteria统计总数
     */
    public int getCountByCriteria(final Criteria criteria) {   
        Integer count = (Integer) criteria.setProjection(Projections.rowCount()).uniqueResult();    
        return count.intValue();   
    }
    
//    /**
//     * 根据DetachedCriteria加载分页，指定页大小和起始位置
//     */
//    public Page findPageByCriteria(final DetachedCriteria detachedCriteria, final int pageSize, final int startIndex) {   
//        return (Page) getHibernateTemplate().execute(new HibernateCallback() {   
//            public Object doInHibernate(Session session) throws HibernateException {   
//                Criteria criteria = detachedCriteria.getExecutableCriteria(session);
//                int totalCount = ((Integer) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();   
//                criteria.setProjection(null);
//                List items = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
//                items = transformResults(items);
//                Page ps = new Page(items, totalCount, pageSize, startIndex);   
//                return ps;   
//            }
//        });   
//    }
    
    /**
     * 根据DetachedCriteria加载分页，默认页大小，从第0条开始
     */
//    public Page findPageByCriteria(final DetachedCriteria detachedCriteria) {   
//        return findPageByCriteria(detachedCriteria, Page.PAGESIZE, 0);   
//    }   
  
    /**
     * 根据DetachedCriteria加载分页，默认页大小，从第startIndex条开始
     */
//    public Page findPageByCriteria(final DetachedCriteria detachedCriteria, final int startIndex) {   
//        return findPageByCriteria(detachedCriteria, Page.PAGESIZE, startIndex);   
//    } 
    
    /**
     * 根据DetachedCriteria统计总数
     */
    public int getCountByCriteria(final DetachedCriteria detachedCriteria) {   
        Integer count = (Integer) getHibernateTemplate().execute(new HibernateCallback() {   
            public Object doInHibernate(Session session) throws HibernateException {   
                Criteria criteria = detachedCriteria.getExecutableCriteria(session);   
                return criteria.setProjection(Projections.rowCount()).uniqueResult();   
            }   
        });   
        return count.intValue();   
    } 
    
    /**
     * 根据hql加载分页，指定页大小和起始位置
     */
    public Page findPageByQuery(final String hql, Searcher searcher, Object...values) {
    	
    	int pageNo = searcher.getPageNo();
    	int pageSize = searcher.getPageSize();
    	
        int totalCount = getCountByQuery(hql, values);
        
        if(pageNo==0)pageNo=1;
		int startIndex = (pageNo - 1) * pageSize;

        Query query = createQuery(hql, values);
        List items = query.setFirstResult(startIndex).setMaxResults(pageSize).list();
        Page ps = new Page(items, searcher.getOrder(), totalCount, pageSize, startIndex);
        return ps;
    }
    
    /**
     * 根据hql加载分页，默认页大小，从第0条开始
     */
    public Page findPageByQuery(final String hql, Object...values) {   
        return findPageByQuery(hql, Page.PAGESIZE, 0, values);   
    }   
 
    /**
     * 根据hql加载分页，默认页大小，从第startIndex条开始
     */
    public Page findPageByQuery(final String hql, final int startIndex, Object...values) {   
        return findPageByQuery(hql, Page.PAGESIZE, startIndex, values);  
    }
    
    /**
     * 通过Finder查询
     */
    public T findUniqueByFinder(Finder finder){
		Query query = createQuery(finder.getHql());
		finder.setParameters(query);
		return (T) query.uniqueResult();
	}
    
    public Map findMapByFinder(Finder finder){
    	Query query = createQuery(finder.getHql());
		finder.setParameters(query);
		return (Map) query.uniqueResult();
    }
    
    /**
     * 通过Finder查询
     */
    public Page findPageByFinder(Finder finder){
    	
//    	int pageNo = searcher.getPageNo();
//    	int pageSize = searcher.getPageSize();
    	
//		if(pageNo==0)pageNo=1;
//		int startIndex = (pageNo - 1) * pageSize;
		
		Query query = createQuery(finder.getHql());
		finder.setParameters(query);
		query.setFirstResult(finder.getFirstResult());
		query.setMaxResults(finder.getMaxResults());
		Page ps = new Page( query.list(), 
				finder.getPageOrder(), 
				countQueryResult(finder), 
				finder.getMaxResults(), 
				finder.getFirstResult());
		return ps;
	}
    
    /**
     * 通过Finder查询
     */
    public List<T> findByFinder(Finder finder){
		Query query = createQuery(finder.getHql());
		finder.setParameters(query);
		return query.list();
	}
    
    /**
     * 根据SQL的Query查出分页
     * @param query
     * @param pageNo
     * @param pageSize
     * @return
     */
    public Page findPageBySQLQuery(SQLQuery query, Class<?> clazz, int count, Searcher searcher){
    	
    	int pageNo = searcher.getPageNo();
    	int pageSize = searcher.getPageSize();
    	
    	
    	if(pageNo==0)pageNo=1;
		int startIndex = (pageNo - 1) * pageSize;
		
		addScalars(query, clazz);
		
		query.setFirstResult(startIndex);
		query.setMaxResults(pageSize);
		
		List list = query.setResultTransformer(Transformers.aliasToBean(clazz)).list();
		
		Page ps = new Page(
				list,searcher.getOrder(), count, pageSize, startIndex);
		
    	return ps;
    }
    
    /**
     * 根据SQL的Query查出列表
     * @param query
     * @param clazz
     * @return
     */
    public List findBySQLQuery(SQLQuery query, Class<?> clazz){
    	addScalars(query, clazz);
    	return query.setResultTransformer(Transformers.aliasToBean(clazz)).list();
    }
    
    /**
     * 自动映射结果集
     * @param query
     * @param clazz
     */
    private void addScalars(SQLQuery query, Class<?> clazz){
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			query.addScalar(field.getName(),getHibernateType(field.getType()));
		}
	}
	
    /**
     * 把JAVA的数据类型转成Hibernate， Hibernate应有这样的方法，但找不到
     * @param type
     * @return
     */
	private NullableType getHibernateType(Class<?> type){
		if(type==Long.class){
			return Hibernate.LONG;
		}else if(type==String.class){
			return Hibernate.STRING;
		}else if(type==Integer.class){
			return Hibernate.INTEGER;
		}else if(type==Double.class){
			return Hibernate.DOUBLE;
		}else if(type == Date.class){
			return Hibernate.TIMESTAMP;
		}
		throw new RuntimeException("未定义的返回类型,Hibernate 扩展定义");
	}

    /**
     * 通过Finder获得查询的总数
     */
    public int countQueryResult(Finder finder){
		Query query = createQuery(finder.getCountHql());
		finder.setParameters(query);
		return Integer.parseInt(query.uniqueResult().toString());
	}
    
    /**
     * 通过SQLQuery获得查询的总数
     */
    public int countSQLQueryResult(SQLQuery query, String countName){
    	 Integer count = (Integer)query
			.addScalar(countName, Hibernate.INTEGER)
			.setFirstResult(0).setMaxResults(1)
			.list()
			.get(0);
    	 return count.intValue();
	}
    
    /**
     * 根据hql统计总数
     */
    public int getCountByQuery(final String hql, Object...values) {   
        String countQueryString = " select count (*) " + removeSelect(removeOrders(hql));
        List countlist = getHibernateTemplate().find(countQueryString, values);
        return Integer.valueOf(String.valueOf(countlist.get(0)));
    }
    
    //创建Criteria和Query
    
    /**
     * 创建Criteria对象
     *
     * @param criterions 可变的Restrictions条件列表
     */
    public Criteria createCriteria(Criterion...criterions) {
        Criteria criteria = getSession().createCriteria(getPojoClass());
        for (Criterion c : criterions)
            criteria.add(c);
        return criteria;
    }

    /**
     * 创建Criteria对象，带排序字段与升降序字段
     */
    public Criteria createCriteria(String orderBy, boolean isAsc, Criterion...criterions) {
        Criteria criteria = createCriteria(criterions);
        if (isAsc)
            criteria.addOrder(Order.asc(orderBy));
        else
            criteria.addOrder(Order.desc(orderBy));
        return criteria;
    }
    
    /**
     * 方法取自SpringSide.
     * 创建Query对象. 对于需要first,max,fetchsize,cache,cacheRegion等诸多设置的函数,可以在返回Query后自行设置.
     * 留意可以连续设置,如下：
     * <pre>
     * dao.getQuery(hql).setMaxResult(100).setCacheable(true).list();
     * </pre>
     * 调用方式如下：
     * <pre>
     *        dao.createQuery(hql)
     *        dao.createQuery(hql,arg0);
     *        dao.createQuery(hql,arg0,arg1);
     *        dao.createQuery(hql,new Object[arg0,arg1,arg2])
     * </pre>
     *
     * @param values 可变参数.
     */
    public Query createQuery(String hql, Object... values) {
        Query query = getSession().createQuery(hql);
        for (int i = 0; i < values.length; i++) {
            query.setParameter(i, values[i]);
        }
        return query;
    }
    
    public SQLQuery createSQLQuery(String sql,Object...values) {
        SQLQuery query = getSession().createSQLQuery(sql);
        for (int i = 0; i < values.length; i++) {
            query.setParameter(i, values[i]);
        }
        return query;
    }
    
    /**
     * 方法取自SpringSide.
     * 去除hql的select子句，未考虑union的情况
     */
    private static String removeSelect(String hql) {
        int beginPos = hql.toLowerCase().indexOf("from");
        return hql.substring(beginPos);
    }

    /**
     * 方法取自SpringSide.
     * 去除hql的orderby子句
     */
    private static String removeOrders(String hql) {
        Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
        Matcher m = p.matcher(hql);
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            m.appendReplacement(sb, "");
        }
        m.appendTail(sb);
        return sb.toString();
    }
    
    /**
     * 将联合查询的结果内容从Map或者Object[]转换为实体类型，如果没有转换必要则直接返回
     */
    private List transformResults(List items) {
        if (items.size() > 0) {
            if (items.get(0) instanceof Map) {
                ArrayList list = new ArrayList(items.size());
                for (int i = 0; i < items.size(); i++) {
                    Map map = (Map)items.get(i);
                    list.add(map.get(CriteriaSpecification.ROOT_ALIAS));
                }
                return list;
            } else if (items.get(0) instanceof Object[]) {
                ArrayList list = new ArrayList(items.size());
                int pos = 0;
                for (int i = 0; i < ((Object[])items.get(0)).length; i++) {
                    if (((Object[])items.get(0))[i].getClass() == getPojoClass()) {
                        pos = i;
                        break;
                    }
                }
                for (int i = 0; i < items.size(); i++) {
                    list.add(((Object[])items.get(i))[pos]);
                }
                return list;
            } else
                return items;
        } else
            return items;
    }
}